package order

import (
	"context"
	"fmt"
	"github.com/gogf/gf/v2/container/garray"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gctx"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/silenceper/wechat/v2/miniprogram/subscribe"
	"hotgo/internal/consts"
	"hotgo/internal/dao"
	"hotgo/internal/library/contexts"
	"hotgo/internal/library/hfsm"
	"hotgo/internal/library/queue"
	"hotgo/internal/library/wechat"
	"hotgo/internal/model/entity"
	"hotgo/internal/model/input/adminin"
	"hotgo/internal/model/input/apiin"
	"hotgo/internal/model/input/sysin"
	"hotgo/internal/service"
	"hotgo/utility/convert"
	"hotgo/utility/simple"
	"math"
	"time"

	"hotgo/api/api/order/v1"
)

func (c *ControllerV1) OrderPay(ctx context.Context, req *v1.OrderPayReq) (res *v1.OrderPayRes, err error) {
	r, err := service.SysUserOrder().OrderPay(ctx, &req.OrderPayInp)
	if err != nil {
		return nil, err
	}
	res = &v1.OrderPayRes{
		AppId:     r.AppId,
		TimeStamp: r.TimeStamp,
		NonceStr:  r.NonceStr,
		Package:   r.Package,
		SignType:  r.SignType,
		PaySign:   r.PaySign,
	}
	return res, nil
}
func (c *ControllerV1) OrderQuery(ctx context.Context, req *v1.OrderQueryReq) (res *v1.OrderQueryRes, err error) {
	list, totalCount, err := service.SysUserOrder().List(ctx, &req.UserOrderListInp)
	res = new(v1.OrderQueryRes)
	res.List = list
	res.PageRes.Pack(req, totalCount)
	return
}
func (c *ControllerV1) UnPayOrderRePay(ctx context.Context, req *v1.UnPayOrderRePayReq) (res *v1.UnPayOrderRePayRes, err error) {
	res = new(v1.UnPayOrderRePayRes)
	rs, err := service.SysUserOrder().RePay(ctx, &sysin.UnPayOrderRePayInp{
		OrderUID: req.OrderUID,
	})
	if err != nil {
		return nil, err
	}
	res.AppId = rs.AppId
	res.TimeStamp = rs.TimeStamp
	res.NonceStr = rs.NonceStr
	res.Package = rs.Package
	res.SignType = rs.SignType
	res.PaySign = rs.PaySign

	return res, nil
}

func (c *ControllerV1) RefundRequest(ctx context.Context, req *v1.RefundRequestReq) (res *v1.RefundRequestRes, err error) {
	rs, err := service.SysUserOrder().RefundRequest(ctx, &sysin.RefundRequestInp{
		OrderUID: req.OrderUID,
		Reason:   req.Reason,
	})
	if err != nil {
		return nil, err
	}
	res = new(v1.RefundRequestRes)
	res.RefundRequestModel = rs
	//res.Id = rs.Id
	//res.Status = rs.Status
	//res.AppId = rs.AppId
	//res.OrderSn = rs.OrderSn
	//res.RefundTradeNo = rs.RefundTradeNo
	//res.RefundMoney = rs.RefundMoney
	//res.RefundWay = rs.RefundWay
	//res.Ip = rs.Ip
	//res.Reason = rs.Reason
	//res.Remark = rs.Remark
	//res.CreatedAt = rs.CreatedAt
	//res.UpdatedAt = rs.UpdatedAt
	return
}

// OrderStatus 修改订单状态
func (c *ControllerV1) OrderStatus(ctx context.Context, req *v1.OrderStatusReq) (res *v1.OrderStatusRes, err error) {

	res = new(v1.OrderStatusRes)
	order, err := service.SysUserOrder().View(ctx, &sysin.UserOrderViewInp{
		Id: req.Id,
	})
	if err != nil {
		return nil, gerror.New("订单异常")
	}
	if order.UserId != contexts.GetUserId(ctx) {
		return nil, gerror.New("无法操作")
	}

	switch req.Status {
	case 1:
		if order.OrderType == consts.OrderTypeGroupOrder {
			if order.Status == consts.UserOrderStatusNot {
				err = hfsm.OFSM.PushState(order.Id, consts.NotToCloseEvent)
				if err != nil {
					return nil, gerror.New("操作失败")
				}
			} else if order.Status == consts.UserOrderStatusWaitVerify {
				_, err = service.SysUserOrder().RefundRequest(ctx, &sysin.RefundRequestInp{
					OrderUID: order.Uid,
					Reason:   "取消订单",
				})
				if err != nil {
					return nil, err
				}
			}
		}
		if order.Status == consts.UserOrderStatusWaitVerify && order.OrderType == consts.OrderTypePointRight {
			err = hfsm.OFSM.PushState(order.Id, consts.WaitVerifyToCloseEvent)
			if err != nil {
				return nil, gerror.New("操作失败")
			}
		}
	case 2:
		_, err := service.SysUserOrder().Update(ctx, &sysin.UserOrderEditInp{
			entity.Order{
				Id:      order.Id,
				Deleted: 1,
			},
		})
		if err != nil {
			return nil, gerror.New("操作失败")
		}
	case 3:
		if order.Status == consts.UserOrderStatusExpressed {

			// 分销
			_ = service.Distribution().PromoPro(gctx.New(), order.Uid)

			// 开通会员
			err = service.AdminMember().OpenMember(ctx, &adminin.OpenMemberInp{
				MemberId: order.UserId,
				OrderID:  order.Id,
				CardId:   order.MemberCardId,
			})

			err = hfsm.OFSM.PushState(order.Id, consts.ExpressedToDoneEvent)
			if err != nil {
				return nil, gerror.New("操作失败")
			}
		}
	}
	//order, err = service.SysUserOrder().View(ctx, &sysin.UserOrderViewInp{
	//	Id: req.Id,
	//})
	//res.UserOrderViewModel = order
	return
}

// OrderDetail 订单详情
func (c *ControllerV1) OrderDetail(ctx context.Context, req *v1.OrderDetailReq) (res *v1.OrderDetailRes, err error) {

	res = new(v1.OrderDetailRes)
	res.UserOrderDetailModel = new(sysin.UserOrderDetailModel)

	// 获取用户信息
	user, err := service.AdminMember().GetById(ctx, &adminin.GetByIdInp{
		Id: contexts.GetUserId(ctx),
	})
	if err != nil || user == nil {
		return nil, gerror.New("获取用户信息失败")
	}

	var userId int64
	if user.RoleId != consts.RoleStore {
		userId = user.Id
	}

	// 获取订单信息
	order, err := service.SysUserOrder().GetOrderByUID(ctx, req.UID, userId)
	if err != nil {
		return nil, gerror.New("获取订单信息失败")
	}

	// 获取订单用户信息
	orderUser, err := service.AdminMember().GetById(ctx, &adminin.GetByIdInp{
		Id: order.UserId,
	})
	if err != nil {
		return nil, gerror.New("获取订单用户信息失败")
	}

	// 获取商品信息
	product, err := service.Product().ProductInfo(ctx, &apiin.ProductInfoInp{
		Lat:       req.Lat,
		Lng:       req.Lng,
		ProductId: order.ProductId,
	})
	if err != nil {
		return nil, gerror.New("获取商品信息失败")
	}

	if user.RoleId == consts.RoleStore {
		// 商户权限验证
		flag, err := service.SysStoreMember().StorePermission(ctx, &sysin.StorePermissionInp{
			StoreId: req.StoreId,
		})
		if err != nil {
			return nil, gerror.New("商户权限验证失败")
		}

		// 门店用户是否能核销该订单
		if user.RoleId == consts.RoleStore && product.Stores != nil {
			for _, store := range product.Stores {
				if store.Id == req.StoreId && flag {
					res.CanVerify = true
					break
				}
			}
		}
	}

	// 设置返回信息
	res.Id = order.Id
	res.Name = product.Name
	res.Nickname = orderUser.Nickname
	res.Avatar = orderUser.Avatar
	res.Uid = order.Uid
	res.OrderType = order.OrderType
	res.CreatedAt = order.CreatedAt
	res.Status = order.Status
	//res.OpenId = user.OpenId
	res.Qty = order.Qty
	res.UseAmount = order.UseAmount
	res.Stores = product.Stores
	res.VerifiedCount = order.VerifiedCount

	return
}

// 订单核销
func (c *ControllerV1) OrderVerify(ctx context.Context, req *v1.OrderVerifyReq) (res *v1.OrderVerifyRes, err error) {

	if contexts.GetRoleId(ctx) != consts.RoleStore {
		return nil, gerror.New("无权限")
	}

	// 商户权限验证
	flag, err := service.SysStoreMember().StorePermission(ctx, &sysin.StorePermissionInp{
		StoreId: req.StoreId,
	})
	if err != nil {
		return nil, gerror.New("商户权限验证失败")
	}
	if !flag {
		return nil, gerror.New("无商户操作权限")
	}

	// 核销状态
	status := false

	// 获取订单信息
	order, err := service.SysUserOrder().GetOrderByUID(ctx, req.UID, 0)
	if err != nil {
		return nil, gerror.New("获取订单信息失败")
	}

	// 非待核销状态返回错误
	if order.Status != consts.UserOrderStatusWaitVerify {
		return nil, gerror.New("订单" + order.Status + "，无法核销")
	}

	// 获取商品信息
	product, err := service.Product().ProductInfo(ctx, &apiin.ProductInfoInp{
		ProductId: order.ProductId,
	})
	if err != nil {
		return nil, gerror.New("获取商品信息失败")
	}

	// 门店用户是否能核销该订单
	if product.Stores != nil {
		for _, store := range product.Stores {
			if store.Id == req.StoreId {
				// 可核销数
				verifiedCount := order.Qty - order.VerifiedCount
				// 判断剩余核销数是否小于需要
				if !(verifiedCount > 0 && (verifiedCount >= req.Qty)) {
					return nil, gerror.New("核销数量不能大于剩余数量")
				} else {
					// 更新订单核销数量
					if err = service.SysUserOrder().VerifiedCount(ctx, &sysin.UserOrderVerifiedCountInp{
						Id:            order.Id,
						VerifiedCount: gconv.Int(req.Qty),
					}); err != nil {
						glog.Errorf(context.Background(), "更新订单核销数量失败: %s", err.Error())
						return
					}

					// 如果核销数量等于需要核销数量，修改订单状态为已完成
					if verifiedCount == req.Qty {
						// 核销，修改状态为已完成
						err = hfsm.OFSM.PushState(order.Id, consts.VerifyToDoneEvent)
						if err != nil {
							return nil, gerror.New("核销操作失败")
						}
						_, err = dao.Order.Ctx(ctx).WherePri(order.Id).Data(g.Map{
							dao.Order.Columns().StoreId:    store.Id,
							dao.Order.Columns().BusinessId: store.BusinessId,
						}).Update()
						if err != nil {
							glog.Errorf(context.Background(), "更新订单门店信息失败: %s", err.Error())
						}

						if order.OrderType == consts.OrderTypeGroupOrder && order.CostPrice > 0 {
							// 增加积分
							if order.UseAmount > 0 {
								if _, err = service.AdminCreditsLog().SaveIntegral(ctx, &adminin.CreditsLogSaveIntegralInp{
									MemberId:    order.UserId,
									OrderID:     order.Id,
									AppId:       contexts.GetModule(ctx),
									CreditGroup: consts.CreditGroupIncr,
									Num:         math.Floor(order.UseAmount), // TODO 人名币积分兑换比例 临时设置
									Remark:      fmt.Sprintf("购买团购: %s，赠送积分", order.ProductName),
								}); err != nil {
									glog.Errorf(context.Background(), "增加积分失败,请稍后重试: %s", err.Error())
								}
							}

							// 商户添加余额
							if order.CostPrice > 0 {
								_, err = service.AdminCreditsLog().SaveBalance(ctx, &adminin.CreditsLogSaveBalanceInp{
									MemberId:    contexts.GetUserId(ctx),
									StoreId:     store.Id,
									AppId:       contexts.GetModule(ctx),
									AddonsName:  contexts.GetAddonName(ctx),
									CreditGroup: consts.CreditGroupIncr,
									Num:         order.CostPrice,
									OrderID:     order.Id,
									Remark:      "【团购】" + product.Name + " * " + gconv.String(order.Qty),
								})
								if err != nil {
									return
								}
							}

							simple.SafeGo(ctx, func(ctx context.Context) {
								// 分销
								service.Distribution().Product(ctx, order.Uid)
							})
						}
					}

					// 通知
					// 推送订阅消息
					// 订单信息
					var orderInfo entity.Order
					if err = dao.Order.Ctx(ctx).WherePri(order.Id).Scan(&orderInfo); err != nil {
						glog.Errorf(ctx, "获取订单信息失败: %s", err.Error())
						return
					}
					// 获取用户
					var memberInfo entity.AdminMember
					if err = dao.AdminMember.Ctx(ctx).WherePri(orderInfo.UserId).Scan(&memberInfo); err != nil {
						glog.Errorf(ctx, "获取用户信息失败: %s", err.Error())
						return
					}
					conf, err2 := service.SysConfig().GetSubMsg(ctx)
					if err2 != nil {
						err = err2
						glog.Errorf(ctx, "获取订阅消息配置失败: %s", err.Error())
						return
					}
					// 获取门店
					if err = wechat.SendSubMsg(ctx, &subscribe.Message{
						ToUser:     memberInfo.OpenId,
						TemplateID: conf.MpAfterVerifiedKey,
						Page:       conf.MpAfterVerifiedPage + "?orderId=" + order.Uid,
						Data: map[string]*subscribe.DataItem{
							"thing1":            {Value: store.Name},
							"date4":             {Value: gtime.Now().Layout(time.DateTime)},
							"thing6":            {Value: product.Name},
							"character_string7": {Value: orderInfo.Uid},
						},
						MiniprogramState: convert.ConvertMpEnvCfg(g.Cfg().MustGet(ctx, "system.envVersion",
							g.Cfg().MustGet(ctx, "system.envVersion", "trial").String()).String()),
						Lang: "zh_CN",
					}); err != nil {
						glog.Debugf(ctx, "配置: %#v", conf)
						glog.Warningf(ctx, "推送订阅消息失败: %s", err.Error())
					}
					if err = queue.DelayPush(consts.QueueAfterVerifiedTopic, memberInfo, int64(conf.MpRetainSec)); err != nil {
						glog.Debugf(ctx, "配置: %#v", conf)
						glog.Warningf(ctx, "延迟推送消息失败: %s", err.Error())
					}
					// 记录
					if err = service.SysOrderVerifyLog().Edit(ctx, &sysin.OrderVerifyLogEditInp{
						OrderVerifyLog: entity.OrderVerifyLog{
							OrderId:    uint64(order.Id),
							StoreId:    uint64(store.Id),
							BusinessId: store.BusinessId,
							MemberId:   uint64(order.UserId),
							ProductId:  uint64(order.ProductId),
							Count:      gconv.Int(req.Qty),
							CreatedBy:  uint64(contexts.GetUserId(ctx)),
						},
					}); err != nil {
						glog.Errorf(ctx, "记录核销日志失败: %s", err.Error())
					}

					status = true

					// 更新用户标签
					err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
						// 修改用户标签
						user, err := service.AdminMember().GetById(ctx, &adminin.GetByIdInp{
							Id: order.UserId,
						})
						if err != nil {
							glog.Errorf(context.Background(), "修改用户标签失败: %s", err.Error())
							return
						}
						label := garray.NewStrArrayFrom(gconv.Strings(user.Label.Array()))
						mb := &adminin.MemberUpdateInfoInp{
							ID: user.Id,
						}
						if label.ContainsI(consts.AdminMemberLabelNewbie) {
							mb.Label = gjson.New([]string{consts.AdminMemberLabelFirstOrder})
						} else if label.ContainsI(consts.AdminMemberLabelFirstOrder) {
							mb.Label = gjson.New([]string{})
						}
						// 更新用户标签
						if err = service.AdminMember().UpdateInfo(ctx, mb); err != nil {
							glog.Errorf(ctx, "修改用户标签失败: %s", err.Error())
							return
						}
						return
					})
				}
			}
		}
	}

	if !status {
		return nil, gerror.New("无法核销\n非本门店订单")
	}
	return
}

// 查询门店核销数量
func (c *ControllerV1) OrderVerifyCount(ctx context.Context, req *v1.OrderVerifyCountReq) (res *v1.OrderVerifyCountRes, err error) {
	res = new(v1.OrderVerifyCountRes)
	// 总数
	count, err := dao.Order.Ctx(ctx).As("o").
		LeftJoin(dao.Product.Table()+" p", "o.product_id = p.id").
		LeftJoin(dao.ProductStore.Table()+" ps", "o.product_id = ps.product_id").
		LeftJoin(dao.Store.Table()+" s", "ps.store_id = s.id").
		Where("o.verified_count > 0").
		Where("s.old_id = ?", req.Id).Sum("o.verified_count")

	// 当天数据
	count2, err := dao.Order.Ctx(ctx).As("o").
		LeftJoin(dao.Product.Table()+" p", "o.product_id = p.id").
		LeftJoin(dao.ProductStore.Table()+" ps", "o.product_id = ps.product_id").
		LeftJoin(dao.Store.Table()+" s", "ps.store_id = s.id").
		Where("o.verified_count > 0").
		Where("o.archive_time > ?", fmt.Sprintf("%d-%02d-%02d", gtime.Now().Year(), gtime.Now().Month(), gtime.Now().Day())).
		Where("s.old_id = ?", req.Id).Sum("o.verified_count")
	res.VerifiedCount = gconv.Int(count)
	res.TodayVerifiedCount = gconv.Int(count2)
	return
}

// 商家订单管理
func (c *ControllerV1) StoreApiOrderList(ctx context.Context, req *v1.StoreApiOrderListReq) (res *v1.StoreApiOrderListRes, err error) {
	list, totalCount, err := service.SysUserOrder().List(ctx, &req.UserOrderListInp)
	if err != nil {
		return nil, err
	}
	res = new(v1.StoreApiOrderListRes)
	res.List = list
	res.PageRes.Pack(req, totalCount)
	return
}
